#pragma once
#include <exception>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "kernels.h"
#include "GridException.h"

#ifdef FAKE_SPRNG
#define SPRNG_LFG 0
#define SPRNG_DEFAULT 0
#define init_sprng(we, dont, need, stinkin, parameters) 0
#define sprng(whocares) drand48()
#else
#include <sprng.h>
#endif

enum GridDeviceType
{
  kCPU,
  kOpenCL
};

extern bool (*initializeRuntime[kGridNumSupportedRuntimes])(void);

class Grid
{
  public:
    Grid(int xDim, 
         int yDim,
         int zDim,
         float lengthScale = -1, 
         GridDeviceType devType = kCPU, 
         int devNum = 0,
         int* randomNumStream = NULL);
    static void initializeRuntime(GridDeviceType dev);
    void computeTotalEnergy(bool useCurrentBuffers = false);
    void perturbGrid();

    //Accessor methods
    float getCurWaveFunction(){return waveFunction[curBuffer];}
    float getProposedWaveFunction()
    {
      return waveFunction[curBuffer == 0 ? 1 : 0];
    }
    float getCurPotential(){return this->potential[curBuffer];}
    float getProposedPotential()
    {
      return this->potential[curBuffer == 0 ? 1 : 0];
    }
    float getCurKinetic(){return this->kinetic[curBuffer];}
    float getProposedKinetic()
    {
      return this->kinetic[curBuffer == 0 ? 1 : 0];
    }

    void setDeviceType(GridDeviceType devType){this->devType = devType;}
    void setDeviceNum(int devNum){this->devNum = devNum;}
    void accept();
    void reject();
    void printMetrics();
    ~Grid();
  protected:
    GridDeviceType devType;
    int devNum;
    static void initializeFunctionTable();
    static bool functionTableInit;
    int* randomNumStream;
    int xDim;
    int yDim;
    int zDim;
    int curBuffer;
    float lengthScale;
    float* x[2];
    float* y[2];
    float* z[2];
    float waveFunction[2];
    float potential[2];
    float kinetic[2];
    int totalIterations;
    int accepts;
};

